﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace DependencyInjection
{
    public class Inject : Attribute
    {
        public Inject() { }
    }

    public class Injector
    {
        private AbstractModule _module;

        private Dictionary<Type, object> _mapSingles = new Dictionary<Type, object>();

        public Injector(AbstractModule module)
        {
            _module = module;
        }

        public object GetInstance(Type type)
        {
            object result = null;

            Type tT = _module.Get(type);

            result = GetSingle(tT);

            if (result == null)
            {
                result = GetNewInstance(tT);
            }

            return result;
        }

        private object GetSingle(Type tImpl)
        {
            object result = null;

            if (_mapSingles.ContainsKey(tImpl) && _module.IsSingle(tImpl))
            {
                result = _mapSingles[tImpl];
            }

            return result;
        }

        private object GetNewInstance(Type tImpl)
        {
            object result = null;

            ConstructorInfo[] consInfos = tImpl.GetConstructors();

            foreach (ConstructorInfo consInfo in consInfos)
            {
                foreach (object oAttribute in consInfo.GetCustomAttributes(false))
                {
                    if (oAttribute is Inject)
                    {
                        ParameterInfo[] pInfos = consInfo.GetParameters();
                        object[] oArguments = new object[pInfos.Count()];

                        int i = 0;
                        foreach (ParameterInfo pInfo in pInfos)
                        {
                            oArguments[i] = this.GetInstance(pInfo.ParameterType);
                            i++;
                        }

                        result = Activator.CreateInstance(tImpl, oArguments); 
                    }
                }

                if (result == null)
                {
                    result = Activator.CreateInstance(tImpl);
                }
            }

            if (_module.IsSingle(tImpl))
            {
                _mapSingles.Add(tImpl, result);
            }

            return result;
        }
    }
}
